
package com.jf.cloud.search.manager;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.jf.cloud.api.dto.EsPageDTO;
import com.jf.cloud.api.order.bo.EsPurchaseOrderBO;
import com.jf.cloud.api.order.bo.EsPurchaseOrderItemBO;
import com.jf.cloud.api.supplier.feign.SupplierDetailFeignClient;
import com.jf.cloud.api.supplier.vo.SupplierApiDetailVO;
import com.jf.cloud.api.vo.EsPageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.i18n.I18nMessage;
import com.jf.cloud.common.i18n.LanguageEnum;
import com.jf.cloud.common.order.dto.PurchaseOrderSearchDTO;
import com.jf.cloud.common.order.vo.EsPurchaseOrderVO;
import com.jf.cloud.common.response.ServerResponseEntity;
import com.jf.cloud.common.util.Json;
import com.jf.cloud.search.constant.EsIndexEnum;
import ma.glasnost.orika.MapperFacade;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author zz
 * @date 2020/11/16
 */
@Component
public class PurchaseOrderSearchManager {

    private static final Logger log = LoggerFactory.getLogger(PurchaseOrderSearchManager.class);


    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private MapperFacade mapperFacade;

    @Autowired
    private SupplierDetailFeignClient supplierDetailFeignClient;

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param param 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsPurchaseOrderVO> page(EsPageDTO pageDTO, PurchaseOrderSearchDTO param) {
        param.setPageSize(pageDTO.getPageSize());
        param.setPageNum(pageDTO.getPageNum());
        EsPageVO<EsPurchaseOrderVO> purchaseOrderPage = pageSearchResult(param);
        loadSupplierInfo(purchaseOrderPage.getList());
        return purchaseOrderPage;
    }

    private void loadSupplierInfo(List<EsPurchaseOrderVO> list) {
        if (CollUtil.isEmpty(list)) {
            return;
        }
        List<Long> supplierIds = list.stream().map(EsPurchaseOrderVO::getSupplierId).collect(Collectors.toList());

        // 根据供应商名称筛选
        ServerResponseEntity<List<SupplierApiDetailVO>> supplierResp = supplierDetailFeignClient.listSupplierDetailBySupplierIds(supplierIds);
        if (!supplierResp.isSuccess()) {
            throw new LuckException(supplierResp.getMsg());
        }
        Map<Long, SupplierApiDetailVO> supplierMap = supplierResp.getData().stream().collect(Collectors.toMap(SupplierApiDetailVO::getSupplierId, supplierApiDetailVO -> supplierApiDetailVO));
        for (EsPurchaseOrderVO purchaseOrderVO : list) {
            SupplierApiDetailVO supplierApiDetailVO = supplierMap.get(purchaseOrderVO.getSupplierId());
            purchaseOrderVO.setSupplierName(supplierApiDetailVO.getSupplierName());
            purchaseOrderVO.setContactPhone(supplierApiDetailVO.getContactPhone());
        }
    }


    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param param 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsPurchaseOrderVO> pageSearchResult(PurchaseOrderSearchDTO param) {
        if (Objects.isNull(param.getPageSize())) {
            param.setPageSize(10);
        }
        if (Objects.isNull(param.getPageNum())) {
            param.setPageNum(1);
        }
        //1、查询订单数据
        SearchResponse response = searchResult(param);
        //2.处理响应的数据
        return pageEsOrderBO(response, param.getPageSize());
    }

    private EsPageVO<EsPurchaseOrderVO> pageEsOrderBO(SearchResponse response, Integer pageSize) {
        EsPageVO<EsPurchaseOrderVO> pageVO = new EsPageVO<>();
        List<EsPurchaseOrderVO> purchaseOrderList = getOrderRefundListByResponse(response.getHits().getHits());
        pageVO.setList(purchaseOrderList);
        buildSearchPage(pageVO, response, pageSize);
        return pageVO;
    }

    /**
     * 构建分页数据
     * @param esPageVO
     * @param response
     * @param pageSize
     */
    private void buildSearchPage(EsPageVO<?> esPageVO, SearchResponse response, Integer pageSize) {
        //总记录数
        long total = response.getHits().getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int)total % pageSize == 0 ?
                (int)total / pageSize : ((int)total / pageSize + 1);
        esPageVO.setPages(totalPages);
    }

    private SearchResponse searchResult(PurchaseOrderSearchDTO param) {
        // 封装检索条件
        SearchRequest searchRequest = buildSearchRequest(param);
        SearchResponse response;
        try {
            //2、执行检索请求
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.info("搜索返回结果：" + response.toString());

        } catch (IOException e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return response;
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    private List<EsPurchaseOrderVO> getOrderRefundListByResponse(SearchHit[] hits) {
        List<EsPurchaseOrderVO> purchaseOrderList = new ArrayList<>();
        Integer lang = I18nMessage.getLang();
        for (SearchHit hit : hits) {
            EsPurchaseOrderBO purchaseOrder = Json.parseObject(hit.getSourceAsString(), EsPurchaseOrderBO.class);
            for (EsPurchaseOrderItemBO purchaseOrderItemBO : purchaseOrder.getPurchaseOrderItems()) {
                if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
                    purchaseOrderItemBO.setSpuName(purchaseOrderItemBO.getSpuNameEn());
                    purchaseOrderItemBO.setSkuName(purchaseOrderItemBO.getSkuNameEn());
                }
                if (StrUtil.isBlank(purchaseOrderItemBO.getSpuName())) {
                    purchaseOrderItemBO.setSpuName(purchaseOrderItemBO.getSpuNameZh());
                }
                if (StrUtil.isBlank(purchaseOrderItemBO.getSkuName())) {
                    purchaseOrderItemBO.setSkuName(purchaseOrderItemBO.getSkuNameZh());
                }
            }
            if (Objects.equals(lang, LanguageEnum.LANGUAGE_EN.getLang())) {
                purchaseOrder.setSpuName(purchaseOrder.getSpuNameEn());
            }
            if (StrUtil.isBlank(purchaseOrder.getSpuName())) {
                purchaseOrder.setSpuName(purchaseOrder.getSpuNameZh());
            }
            purchaseOrderList.add(mapperFacade.map(purchaseOrder, EsPurchaseOrderVO.class));
        }
        return purchaseOrderList;
    }


    /**
     * 准备检索请求
     *
     * @param param   搜索参数
     * @return
     */
    private SearchRequest buildSearchRequest(PurchaseOrderSearchDTO param) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 关键字搜索
        keywordSearch(param, boolQueryBuilder);

        // 排序
        sort(searchSourceBuilder, boolQueryBuilder);

        if (Objects.nonNull(param.getPageNum()) && Objects.nonNull(param.getPageSize())) {
            //分页
            searchSourceBuilder.from((param.getPageNum()-1) * param.getPageSize());
            searchSourceBuilder.size(param.getPageSize());
        }

        log.info("构建的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.PURCHASE_ORDER.value()}, searchSourceBuilder);
    }


    /**
     * 关键字搜索
     */
    private void keywordSearch(PurchaseOrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) {

        // 创建查询语句 ES中must和should不能同时使用 同时使用should失效 嵌套多个must 将should条件拼接在一个must中即可


        BoolQueryBuilder keywordShouldQuery = QueryBuilders.boolQuery();

        // 店铺名称
        if (StrUtil.isNotBlank(param.getShopName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("shopName", param.getShopName()));
        }

        // 供应商名称
        if (StrUtil.isNotBlank(param.getSupplierName())) {
            keywordShouldQuery.should(QueryBuilders.matchQuery("supplierName", param.getSupplierName()));
        }

        // 商品名称
        if (StrUtil.isNotBlank(param.getSpuName())) {
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            boolQuery.should(QueryBuilders.matchQuery("purchaseOrderItems.spuNameZh", param.getSpuName()));
            boolQuery.should(QueryBuilders.matchQuery("purchaseOrderItems.spuNameEn", param.getSpuName()));
            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("purchaseOrderItems", boolQuery, ScoreMode.None);
            boolQueryBuilder.must(nestedQueryBuilder);
        }

        boolQueryBuilder.must(keywordShouldQuery);
    }

    /**
     * 进行排序
     */
    private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 过滤查询条件，如果有必要的话
     *
     * @param param            查询条件
     * @param boolQueryBuilder 组合进boolQueryBuilder
     */
    private void filterQueryIfNecessary(PurchaseOrderSearchDTO param, BoolQueryBuilder boolQueryBuilder) {
        // 店铺id
        if (Objects.nonNull(param.getShopId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopId()));
        }
        // 店铺id列表
        if (CollUtil.isNotEmpty(param.getShopIds())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopIds()));
        }
        // 供应商id
        if (Objects.nonNull(param.getSupplierId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("supplierId", param.getSupplierId()));
        }
        // 供应商id列表
        if (CollUtil.isNotEmpty(param.getSupplierIds())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("supplierId", param.getSupplierId()));
        }
        // 供应商id列表
        if (Objects.nonNull(param.getPurchaseOrderId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("purchaseOrderId", param.getPurchaseOrderId()));
        }
        // 联系人
        if (StrUtil.isNotBlank(param.getConsignee())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("consignee", param.getConsignee()));
        }
        // 手机号
        if (StrUtil.isNotBlank(param.getMobile())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("mobile", param.getMobile()));
        }

        // 创建时间:  开始时间 - 结束时间
        if (param.getStartTime() != null || param.getEndTime() != null) {
            String createTime = "createTime";
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(createTime);
            if (param.getStartTime() != null) {
                rangeQueryBuilder.gte(param.getStartTime());
            }
            if (param.getEndTime() != null) {
                rangeQueryBuilder.lte(param.getEndTime());
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }

        // 状态 0:已作废 1:待入库 2:已发货 3.部分入库 5:已完成
        if (Objects.nonNull(param.getStatus())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("status", param.getStatus()));
        }

        // sku编码
        if (StrUtil.isNotBlank(param.getPartyCodes())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("partyCode", param.getPartyCodes()));
        }

        // 商品编码
        if (StrUtil.isNotBlank(param.getSpuCodes())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("spuCode", param.getSpuCodes()));
        }
    }
}
